package com.scs.application.core.utils;

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.entity.result.ExcelVerifyHandlerResult;
import cn.afterturn.easypoi.excel.export.ExcelExportService;
import cn.afterturn.easypoi.excel.export.styler.ExcelExportStylerDefaultImpl;
import cn.afterturn.easypoi.excel.imports.ExcelImportService;
import cn.afterturn.easypoi.handler.inter.IExcelDataHandler;
import com.scs.application.core.excel.handler.ExcelDictHandlerImpl;
import com.scs.application.core.exceptions.ExportException;
import com.scs.application.core.utils.excel.ExcelExportStylerImpl;
import com.scs.application.core.utils.excel.annotation.ExcelComment;
import com.scs.application.core.utils.excel.handler.AggregationDataHandler;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.List;

/**
 * @Description:
 */
@Slf4j
public class PoiUtils {

    public static void exportExcel(OutputStream os, String title, Class tClass, List data) {
        exportExcel(os, title, title, tClass, data, false);
    }

    public static void exportExcel(OutputStream os, String title, Class tClass, List data, boolean ignoreError) {
        exportExcel(os, title, title, tClass, data, ignoreError);
    }

    public static void exportExcel(OutputStream os, String title, String sheetName, Class tClass, List data) {
        exportExcel(os, title, sheetName, tClass, data, false);
    }

    public static void exportExcel(OutputStream os, String title, String sheetName, Class tClass, List data, boolean ignoreError) {
        ExportParams params = new ExportParams(null, sheetName, ExcelType.XSSF);
        params.setDictHandler(ExcelDictHandlerImpl.getInstance());

        if (ignoreError) {
            params.setExclusions(new String[]{"错误信息", "错误行号"});
        }

        exportExcel(os, params, tClass, data);
    }

    public static void exportExcel(OutputStream os, ExportParams params, Class tClass, List data) {

        Class<?> style = params.getStyle();
        if (style == ExcelExportStylerDefaultImpl.class) {
            params.setStyle(ExcelExportStylerImpl.class);
        }

        try (Workbook workbook = createWorkbook(params)) {
            new ExcelExportService().createSheet(workbook, params, tClass, data);
            workbook.write(os);
        } catch (IOException e) {
            throw new ExportException("导出失败", e);
        }
    }

    private static Workbook createWorkbook(ExportParams params) {
        if (params.getType() == ExcelType.HSSF) {
            return new HSSFWorkbook();
        } else {
            return new XSSFWorkbook();
        }
    }


    private final static Validator VALIDATOR;

    static {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        VALIDATOR = factory.getValidator();
    }

    /**
     * 获取默认的导入参数
     *
     * @return
     */
    public static ImportParams getDefaultImportParams() {

        ImportParams importParams = new ImportParams();
        importParams.setDictHandler(ExcelDictHandlerImpl.getInstance());
        importParams.setTitleRows(0);
        importParams.setHeadRows(1);
        importParams.setNeedVerify(false);
        importParams.setConcurrentTask(true);
        importParams.setVerifyHandler((object) -> {
            try {
                BeanValidators.validateWithException(VALIDATOR, object);
                return new ExcelVerifyHandlerResult(true);
            } catch (ConstraintViolationException e) {
                List<String> messages = BeanValidators.extractMessage(e);
                return new ExcelVerifyHandlerResult(false, StringUtils.join(messages, ","));
            }
        });
        importParams.setDataHandler(new AggregationDataHandler(
                Lists.newArrayList(new IExcelDataHandler[]{}))
        );
        return importParams;
    }

    public static ExportParams getDefaultExportParams(String title, String sheetName, boolean ignoreError) {
        ExportParams params = new ExportParams(null, sheetName, ExcelType.XSSF);
        params.setDictHandler(ExcelDictHandlerImpl.getInstance());

        if (ignoreError) {
            params.setExclusions(new String[]{"错误行号", "错误消息"});
        }
        return params;
    }

    /**
     * Excel 导入 数据源IO流 字段类型 Integer,Long,Double,Date,String,Boolean
     * 支持校验,支持Key-Value
     *
     * @param inputstream
     * @param pojoClass
     * @param params
     * @return
     * @throws Exception
     */
    public static <T> ExcelImportResult<T> importExcelMore(InputStream inputstream,
                                                           Class<?> pojoClass,
                                                           ImportParams params) throws Exception {
        return new ExcelImportService().importExcelByIs(inputstream, pojoClass, params, true);
    }


    /**
     * 根据 {@code tClass} 相关成员变量的 {@link ExcelComment#example()} 字段创建模拟数据，暂不支持 复杂类型
     * @param tClass
     * @return
     */
    public static <T> T mockData(Class<T> tClass) {
        if (tClass == null) {
            return null;
        }
        T instance = null;

        try {
            instance = tClass.newInstance();
            Field[] declaredFields = tClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                ExcelComment comment = declaredField.getAnnotation(ExcelComment.class);
                if (comment == null) {
                    continue;
                }
                declaredField.setAccessible(true);
                Class<?> fieldType = declaredField.getType();

                String exampleValue = comment.example();
                Object value = null;

                if (fieldType == int.class || fieldType == Integer.class) {
                    value = StringUtils.isBlank(exampleValue) ? 0 : Integer.parseInt(exampleValue);
                } else if (fieldType == short.class || fieldType == Short.class) {
                    value = StringUtils.isBlank(exampleValue) ? 0 : Short.parseShort(exampleValue);
                } else if (fieldType == long.class || fieldType == Long.class) {
                    value = StringUtils.isBlank(exampleValue) ? 0 : Long.parseLong(exampleValue);
                } else if (fieldType == double.class || fieldType == Double.class) {
                    value = StringUtils.isBlank(exampleValue) ? 0 : Double.parseDouble(exampleValue);
                } else if (fieldType == boolean.class || fieldType == Boolean.class) {
                    value = StringUtils.isNotBlank(exampleValue) && Boolean.parseBoolean(exampleValue);
                } else if (fieldType == String.class) {
                    value = exampleValue;
                } else if (fieldType == Date.class) {
                    value = DateUtils.parse(exampleValue);
                }

                if (value == null && !isBaseType(fieldType)) {
                    declaredField.set(instance, null);
                } else {
                    declaredField.set(instance, value);
                }
            }
        } catch (Exception e) {
            log.error("数据构造失败，请查询详细信息", e);
            return instance;
        }

        return instance;
    }


    /**
     * 是否是基础数据类型
     *
     * @param className
     * @return
     */
    private static boolean isBaseType(Class<?> className) {
        if (className.equals(java.lang.Integer.class) ||
                className.equals(java.lang.Byte.class) ||
                className.equals(java.lang.Long.class) ||
                className.equals(java.lang.Double.class) ||
                className.equals(java.lang.Float.class) ||
                className.equals(java.lang.Character.class) ||
                className.equals(java.lang.Short.class) ||
                className.equals(java.lang.Boolean.class)) {
            return true;
        }
        return false;
    }
}

